package com.azier.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * 利用反射进行操作的一个工具类
 */
public final class ReflectUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectUtil.class);

    private ReflectUtil(){
    }

    private static boolean fieldNameInList(String fieldName, List<Field> list) {
        for (int i = 0; i < list.size(); i++) {
            Field f = list.get(i);
            if (fieldName.equals(f.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取 Class 中所有 Field ， 包含父类的 Field
     */
    public static List<Field> getFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<Field>();
        Field[] fieldArray = clazz.getDeclaredFields();
        for (Field f : fieldArray) {
            if (!"serialVersionUID".equalsIgnoreCase(f.getName())) {
                fields.add(f);
            }
        }
        if (clazz.getSuperclass() != Object.class) {
            List<Field> subfields = getFields(clazz.getSuperclass());
            for (int i = 0; i < subfields.size(); i++) {
                Field sf = subfields.get(i);
                if (!fieldNameInList(sf.getName(), fields) && !"serialVersionUID".equalsIgnoreCase(sf.getName())) // 排除同名的父类属性
                {
                    fields.add(sf);
                }
            }
        }
        return fields;
    }

    /**
     * 利用反射获取指定对象的指定属性
     *
     * @param obj 目标对象
     * @param fieldName 目标属性
     * @return 目标属性的值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        Object result = null;
        Field field = ReflectUtil.getField(obj, fieldName);
        if (field != null) {
            try {
                field.setAccessible(true);
                result = field.get(obj);
            } catch (Exception e) {
                LOGGER.warn(e.getMessage(), e);
            }
        }
        return result;
    }

    /**
     * 利用反射获取指定对象里面的指定属性
     *
     * @param obj 目标对象
     * @param fieldName 目标属性
     * @return 目标字段
     */
    private static Field getField(Object obj, String fieldName) {
        Field field = null;
        for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                break;
            } catch (NoSuchFieldException e) {
                // Igore
            }
        }
        return field;
    }

    /**
     * 利用反射设置指定对象的指定属性为指定的值
     *
     * @param field 目标属性
     * @param target 目标对象
     * @param value 目标值
     */
    public static void setFieldValue(Field field, Object target, Object value) {
        try {
            field.setAccessible(true);
            field.set(target, value);
        } catch (IllegalAccessException ex) {
            throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": "
                                            + ex.getMessage());
        }
    }

    /**
     * 利用反射设置指定对象的指定属性为指定的值
     *
     * @param fieldName 目标属性
     * @param target 目标对象
     * @param value 目标值
     */
    public static void setFieldValue(String fieldName, Object target, Object value) {
        Field field = ReflectUtil.getField(target, fieldName);
        if (field != null) {
            try {
                field.setAccessible(true);
                field.set(target, value);
            } catch (IllegalAccessException ex) {
                throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": "
                                                + ex.getMessage());
            }
        }
    }

    /**
     * 得到指定类型的指定位置的泛型实参
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> findParameterizedType(Class<?> clazz) {
        Type parameterizedType = clazz.getGenericSuperclass();
        // CGLUB subclass target object(泛型在父类上)
        if (!(parameterizedType instanceof ParameterizedType)) {
            parameterizedType = clazz.getSuperclass().getGenericSuperclass();
        }
        if (!(parameterizedType instanceof ParameterizedType)) {
            return null;
        }
        Type[] actualTypeArguments = ((ParameterizedType) parameterizedType).getActualTypeArguments();
        if (actualTypeArguments == null || actualTypeArguments.length == 0) {
            return null;
        }
        return (Class<T>) actualTypeArguments[0];
    }
}
